#include "node_opengl_test.h"

//static GLuint VBO,VAO,EBO;
NodeOpenGLTest::NodeOpenGLTest(QWidget *parent): QOpenGLWidget(parent)
{

}

NodeOpenGLTest::~NodeOpenGLTest()
{
    glDeleteVertexArrays(1,&VAO);
    glDeleteBuffers(1,&VBO);
}

/*
https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/
https://blog.csdn.net/qq_36383623/article/details/85123077

顶点数组对象 VertexArrayObject
顶点缓冲对象 VertexBufferObject
索引缓冲对象 ElementBufferObject

Graphics Pipeline，大多译为管线，
实际上指的是一堆原始图形数据途经一个输送管道，期间经过各种变化处理最终出现在屏幕的过程
图形渲染管线可以被划分为两个主要部分：
第一部分把你的3D坐标转换为2D坐标，第二部分是把2D坐标转变为实际的有颜色的像素。

管线的每个阶段
顶点着色器VertexShader：将顶点的3D坐标转化为另一种3D坐标，同时允许我们对顶点属性进行一些基本处理
图元装配ShapeAssembly：将所有的点装配成指定图元的形状
几合着色器GeometryShader：图元形式的一系列顶点的集合作为输入，它可以通过产生新顶点构造出新的（或是其它的）图元来生成其他形状
光栅化Rasterization
片段着色器FragmentShader：计算一个像素的最终颜色
测试与混合TestsAndBlending：判断这个像素是其它物体的前面还是后面，决定是否应该丢弃，也会检查alpha值并对物体进行混合

// 给定了一些数据，比如这里的一系列顶点标准和颜色值，还需要让GL知道这些数据要渲染出什么东西
// 可以是一系列三角行组成的图形，可以是点阵，也可以是渲染成线、
// 给出的命令叫做图元Primitive，基本图形元素：GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP
// 任何绘制指令的调用都把图元传递给GL


// 一旦顶点坐标已经在顶点着色器中处理过，它们就应该是标准化设备坐标了，
// 标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间
//任何落在范围外的坐标都会被丢弃/裁剪，不会显示在你的屏幕上。
// 标准化设备坐标接着会变换为屏幕空间坐标，这是通过glViewport函数提供的数据，进行视口变换(Viewport Transform)完成的


*/

void NodeOpenGLTest::initializeGL()
{
    this->initializeOpenGLFunctions();

    bool success = shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,"D:/Object/QtCpp/QtNodeEditor/src/opengl/test1.vert");
    if(!success){
        qDebug()<<"shaderProgram load vertx file failed"<<shaderProgram.log();
        return;
    }
    success = shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,"D:/Object/QtCpp/QtNodeEditor/src/opengl/test1.frag");
    if(!success){
        qDebug()<<"shaderProgram load frag file failed"<<shaderProgram.log();
        return;
    }
    success = shaderProgram.link();
    if(!success){
        qDebug()<<"shaderProgram link failed"<<shaderProgram.log();
        //return;
    }


    // 以数组形式传递3个3d坐标，这个数组叫顶点数据
    float vertices[] = {
        -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.0f,  0.5f, 0.0f,
         1.0f,  0.5f, 0.0f
    };  // 定义的是二维图像，所以z轴（depth）为0
    // 定义顶点数据之后，需要发送给顶点着色器
    // 顶点着色器会在GPU上创建内存用于储存顶点数据。还要配置GL如何解释这些内存，指定如何发生给显卡
    // 顶点着色器接着会处理我们在内存中指定数量的顶点

    // 通过VBO来管理显存，它会在显存中储存大量顶点。使用缓冲对象使我们可以一次性发送大量数据到显卡
    // 而不需要在传递数据上花费更多时间
    // 使用glGenBuffers和一个缓冲ID生成VBO对象
    // glGenBuffers(GLsizei n,GLuint * buffers); 第一个参数是要生成的缓冲对象的数量，第二个是要输入用来存储缓冲对象名称的数组
/*
GLuint vbo[3];
glGenBuffers(3,vbo)
glGenBuffers()函数仅仅是生成一个缓冲对象的名称，这个缓冲对象并不具备任何意义，
它仅仅是个缓冲对象，还不是一个顶点数组缓冲，
它类似于C语言中的一个指针变量，我们可以分配内存对象并且用它的名称来引用这个内存对象。
OpenGL有很多缓冲对象类型，那么这个缓冲对象到底是什么类型，就要用到下面的glBindBuffer()函数了
*/
    glGenBuffers(1,&VBO);   // 创建对象
    // 绑定缓冲对象类型，VB为GL_ARRAY_BUFFER。GL允许我们同时绑定多个缓冲，只要是不同的缓冲类型
    glBindBuffer(GL_ARRAY_BUFFER, VBO);   // 绑定类型
    // 第四个参数指定了我们希望显卡如何管理给定的数据
    // GL_STATIC_DRAW ：数据不会或几乎不会改变
    // GL_DYNAMIC_DRAW：数据会被改变很多
    // GL_STREAM_DRAW ：数据每次绘制时都会改变
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //设置数据
    // 现在已经把顶点数据储存在显卡的内存中，用VBO这个顶点缓冲对象管理





    unsigned int indices[]={
        0, 1, 2,
        1, 2, 3
    };

    glGenVertexArrays(1,&VAO);
    glBindVertexArray(VAO);
    // VAO绑定时正在绑定的索引缓冲对象会被保存为VAO的元素缓冲对象。绑定VAO的同时也会自动绑定EBO

    glGenBuffers(1,&EBO);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    // 如何解析顶点数据（应用到逐个顶点属性上）
    // 1指定要配置的顶点属性
    // 2指定顶点属性的大小
    // 3指定数据的类型
    // 4是否希望数据被标准化(Normalize)
    // 5步长(Stride),在连续的顶点属性组之间的间隔
    // 简单说就是从这个属性第二次出现的地方到整个数组0位置之间有多少字节
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof (GLfloat),(void*)0);
    glEnableVertexAttribArray(0);



    glBindBuffer(GL_ARRAY_BUFFER, 0); //取消VBO的绑定, glVertexAttribPointer已经把顶点属性关联到顶点缓冲对象了
    glBindVertexArray(0); // //取消VAO绑定

    //线框模式
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

void NodeOpenGLTest::resizeGL(int w, int h)
{
    glViewport(0,0,w,h);
}

void NodeOpenGLTest::paintGL()
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    shaderProgram.bind();
    glBindVertexArray(VAO);
    // glDrawArrays传输或指定的数据是最终的真实数据,在绘制时效能更好
    // glDrawElements指定的是真实数据的调用索引,在内存/显存占用上更节省
    // 另外在传连边数据时是不一样的
    //glDrawArrays(GL_TRIANGLES,0,3);  // 起始索引0，绘制3顶点
    // 用EBO就用elements
    glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);

    shaderProgram.release();
}














